/*
	Title: xobject	

	Este archivo se encarga de los efectos gráficos (animaciones).
	La clase XObject alamacena un div en su interior - con todas sus propiedade - y es capaz de modificarlo de forma gradual.
	
	Por un lado, la clase incluye métodos para averiguar y modificar las propiedades de los contenedores (setStyle getStyle).

	Los cambios de estilo se realizarán de la siguiente forma :
		La clase calcula todos los números intermedios entre dos cantidades, con un incremento constante y cada paso dado en un intervalo de tiempo:

		(start code)
		0 --> 100 INC = 10.
		0 - 10 - 20 - 30 - 40 -50 -60 - 70 - 80 - 90 -100.
		(end code)

		En cada paso intermedio se llama a una función con ese valor como parámetro.
		Dicha función será la que cambie el estilo que deseemos:

		(start code)
		1 - 100 Width.

		width = 0;
		width = 10
		....

		(end code)


		También se pueden hacer loops, bucles para que cuando acabe, vuelva al principio.

		(start code)
		0 --> 100 --> 0 --> 100
		(end)

		Con ello, se consigue un efecto como el del crono, que parpadea.

	Efectos de 'Rebote' (setPercEffect). Para este tipo de efectos, creamos una cola interna en la que se añaden nuevos cambios de estilo para dar sensación de que el elemento llega al final, vuelve en un tanto por ciento 
	de nuevo al final (y esto repetido una serie de veces).

	(start code)
	(ej. 10px -> 200px:   200px -> 190px -> 200px -> 195px -> 200px).
	(end)
*/ 


/* 	Constant: Tabla hexadecimal
	Se trata de traduciones hexadecimal

	(start code)
	A -> 0
	B -> 1
	...
	(end)
*/
var HEX_TABLE =
	{
		'0': 0,
		'1': 1,
		'2': 2,
		'3': 3,
		'4': 4,
		'5': 5,
		'6': 6,
		'7': 7,
		'8': 8,
		'9': 9,
		'A': 10,
		'B': 11,
		'C': 12,
		'D': 13,
		'E': 14,
		'F': 15
	};
		

/* 	Class: XObject

	Esta es la clase encargada de todos los efectos gráficos.
	A partir de un DIV del html, aplicamos diversos efectos gráficos.
*/

/* 	Constructor: XObject
	- Guarda el div que controla.
	- Obtiene datos del CSS sobre dicho div.
  	- Obtiene un array con el estilo del div (cada atributo).
	- Inicializa los timeouts
	
	Parámetros:
	content - El div con el que tratamos.

	Ejemplos:
	(start code)
	var div = document.getElementById("my_div_id");
	var xobj = new XObject(div);
	(end)
 */
function XObject(content)
{
	if(content)
	{
		/* 	Variable: div_cont
			Contenido de div
		*/
		this.div_cont = content;
	
		this.getCSS();
		/* 	Variable: style
			Un array que almacena el estilo que tiene el div en cuanto a los siguienets atributos :
			- opacity
			- width
			- height
			- abs_pos_x
			- abs_pos_y
			- rel_pos_x
			- rel_pos_y
			- backgroundColor
			- borderColor
			- borderWidth
			- color
		*/
		this.style =  
		{
			opacity:				this.getStyle("opacity"),
			
			width:					this.getStyle("width"),
			height:					this.getStyle("height"),

			abs_pos_x:				this.getStyle("abs_pos_x"),
			abs_pos_y:				this.getStyle("abs_pos_y"),

			rel_pos_x:				this.getStyle("rel_pos_x"),
			rel_pos_y:				this.getStyle("rel_pos_y"),

			backgroundColor:		this.getStyle("backgroundColor"),
			borderColor:			this.getStyle("borderColor"),
			borderWidth:			this.getStyle("borderWidth"),

			color:					this.getStyle("color")
		};

		/* 	Variable: timeouts
		 	Timeouts de todas las animaciones, cuando se llama una, si hay otra antes la para.
		*/
		this.timeouts = 
		{
			"opacity":				false,
			
			"width":				false,
			"height":				false,

			"abs_pos_x":			false,
			"abs_pos_y":			false,

			"rel_pos_x":			false,
			"rel_pos_y":			false,

			"backgroundColor":		false,
			"borderColor":			false,
			"borderWidth":			false,
			
			"color":				false
		}
	
		/* 	Constant: SCALE_TIME
		 	Tiempo de los intervalos.
		*/
		this.SCALE_TIME = 10;


	}
}

XObject.prototype = {

	/* 	Function: getStyle

		Esta función coge un estilo del CSS y lo devuelve como resultado
		
		Parámetros:
		attribute - Se trata del atributo que queremos que capte su estilo (Debe ser uno aceptado por <style>)

		Devuelve:
		Estilo correspondiente al atributo.

		Ejemplos:
		(start code)
		alert(xobj.getStyle("opacity"));
		// Mostrará un alert con la opacidad de nuestro XObject
		(end)
	*/
	getStyle: function(attribute)
	{
		var res = 0;
		var style_src = this.css // this.css && false? this.css : this.div_cont.style;

		switch(attribute)
		{
			case "opacity":
				if(navigator.userAgent.toLowerCase().indexOf('msie') == -1)
					res = style_src.opacity * 100;
				else
					res = style_src.filter;

				if(!res)
					res = "100"

				break;

			case "width":
				res = style_src.width && style_src.width != "auto" ? style_src.width : "0px";
	
				break;

			case "height":
				res = style_src.height && style_src.height != "auto" ? style_src.height : "0px";

				break;

			case "abs_pos_x":
				res = style_src.left && style_src.left != "auto" ? style_src.left : "0px";

				break;

			case "abs_pos_y":
				res = style_src.top && style_src.top != "auto" ? style_src.top : "0px";

				break;

			case "rel_pos_x":
				res = style_src.marginLeft && style_src.marginLeft != "auto" ? style_src.marginLeft : "0px";

				break;

			case "rel_pos_y":
				res = style_src.marginTop && style_src.marginTop != "auto" ? style_src.marginTop : "0px";

				break;

			case "backgroundColor":
			case "borderColor":
			case "color":
				var res_rgb = this.getRGB(style_src[attribute]);
				res = res_rgb ? res_rgb : "rgb(255, 255, 255)";

				// Parche Chrome, Safari: rgba(n, n, n, n):

				if(res.match(/rgba./g))
				{
					var cols = this.getNumeric(res);
					res = "rgb(" + cols[0] + ", " + cols[1] + ", " + cols[2] + ")";
				}

				break;

			case "borderWidth":
				if(style_src.borderWidth)
				{
					switch(style_src.borderWidth)
					{
						case "thin":
							res = "1px";
							break;

						case "medium":
							res = "2px";
							break;

						case "thick":
							res = "3px";
							break;
		
						default:
							res = style_src.borderWidth;
					}
				}
				else
					res = "0px";
				
				break;

			default:
				res = style_src[attribute] ? style_src[attribute] : "0px";

				break;

		}

		return res;
	},


	/* 	Function: setStyle

		Esta función pone el estilo que queramos al atributo elegido.
		Sirve para cambiar *de forma unitaria* un atributo de nuestro div.
		
		Parámetros:
		attribute - Se trata del atributo que queremos cambiar (Debe ser uno aceptado por <style>).
		value - Valor que queremos que tome el atributo.

		Devuelve:
		Nada.

		Ejemplos:
		(start code)
		xobj.setStyle("opacity", 50);
		// Cambiará la opacidad del div al 50%, en un único paso.
		(end)
	*/
	setStyle: function(attribute, value)
	{
		var style_src = this.div_cont.style; // this.css && false ? this.css : this.div_cont.style;

		if(value || value == 0)
		{
			this.style[attribute] = value;

			switch(attribute)
			{
				case "opacity":
					if(navigator.userAgent.toLowerCase().indexOf('msie') == -1)
						style_src.opacity = value / 100.0;
					else
						style_src.filter = "progid:DXImageTransform.Microsoft.Alpha(opacity=" + String(value) + ")";

					break;

				case "abs_pos_x":
					style_src.left = value;
		
					break;

				case "abs_pos_y":
					style_src.top = value;
		
					break;

				case "rel_pos_x":
					style_src.marginLeft = value;
		
					break;

				case "rel_pos_y":
					style_src.marginTop = value;
		
					break;

				default:
					style_src[attribute] = value;
	
					break;
			}
		}

	},

	/* 	Function: startAnimation

		Se trata de una función de *BAJO NIVEL* que empieza una animación.
		Es de bajo nivel por que el usuario *NUNCA LLAMARÁ A ESTA FUNCIÓN*, ya que hay creadas otras funciones más sencillas de usar que llaman a ésta.
		La función llama a otra función (<getNextStep>) una serie de veces

		Parámetros:
		attribute - Atributo que queremos que inicie la animación.
		final_value - Valor con el que ha de acabar la animación *(sin unidades)*.
		step - Incremento, cuánto va a ir incrementando el atributo en cada paso.
		func - Función que se llamará en cada paso (que deberá representar la animación).
		fifo - Cola de funciones (ver <xFuncFIFO>).
		loop - *booleano*. Indica si queremos que la animación se meta en un bucle infinito.

		Pasos:
		- Si había otra animación para el mismo atributo, la para.
		- Adquiere el valor actual de nuestro div para dicho atributo.
		- Si se va a realizar algún paso, llama al primer paso, quién llamará al segundo y así sucesivamente.
		- Si no se va a realizar ningún paso, llama a la siguiente función de la Cola <xFuncFIFO>
		

		Devuelve:
		Nada.

		Ejemplos:
		(start code)
		fifo = new xFuncFIFO;
		xobj.startAnimation("width", 50, 2, function(xobj, new_value) { xobj.setStyle("opacity", new_value + "px"); }, fifo, false);
		// Creará una animación en la que la anchura del div irá creciendo de 2 en 2.
		// Además al final llamará a la siguiente función de la cola fifo.
		(end)
	*/
	startAnimation: function(attribute, final_value, step, func, fifo, loop)
	{
		if(this.timeouts[attribute])
			window.clearTimeout(this.timeouts[attribute]);

		var value = this.getNumeric(this.style[attribute]);

		if(loop == true)
			loop = value;
		else
			loop = -1;

		if(final_value - value < 0)
			step = Number(-step);

		var self = this;

		if(step != 0 && ((final_value - value) / step) > 0)
		{
			this.timeouts[attribute] = setTimeout(function()
												{
													self.getNextStep(attribute, value, final_value, step, func, fifo, loop);
												}, this.SCALE_TIME);
		}
		else
		{
			if(fifo)
				fifo.next();
		}
	},

	/* 	Function: getNextStep
		
		Se trata de una función de *BAJO NIVEL*, a la cuál llamará la función <startAnimation>.
		Es de bajo nivel por que el usuario *NUNCA LLAMARÁ A ESTA FUNCIÓN*.
		Esta función es llamada cada 'paso' de la animación. Es decir, si la animación tiene 20 pasos, esta función será llamada 20 veces.
		Es la encargada de cambiar los atributos del div, por medio de una funcion; y de llamarse recursivamente en caso de haber más pasos.		

		Parámetros:
		attribute - Atributo que queremos que inicie la animación.
		value - Valor del atributo en el momento actual *(sin unidades)*
		final_value - Valor con el que ha de acabar la animación *(sin unidades)*.
		step - Incremento, cuánto va a ir incrementando el atributo en cada paso.
		func - Función que se llamará en cada paso (que deberá representar la animación).
		fifo - Cola de funciones (ver <xFuncFIFO>).
		loop - *int* Es el valor Inicial de la animación (esto se evalua automáticamente en <startAnimation>.

		Pasos:
		- Evaluar si hay que continuar.

		_Sí_ : 
		- Calcular próximo valor de la animación.
		- Llamar a la función "func" con dicho valor.
		- Llamar recursivamente a <getNextStep> con los nuevos datos.

		_No_ :
		- Si 'loop' está activado, volver a empezar la animacíon desde el final_value hasta el value inicial de <startAnimation>
		- Si no hay loop llamar a la siguiente función de la fifo (<xFuncFIFO>).

		Devuelve:
		Nada.

		Ejemplos:
		No tiene sentido poner ejemplos, ya que el usuario nunca tendrá que utilizarla.
	*/
	getNextStep: function(attribute, value, final_value, step, func, fifo, loop_value)
	{
		var cont = ((final_value - value) / step);
		var self = this;
		
		if(cont > 0)
		{
			var next_value;

			if(cont >= 1)
				next_value = Number(Number(value) + Number(step));
			else
				next_value = Number(final_value);
	
			func.call(this, next_value);

			if(this.timeouts[attribute])
			{
				this.timeouts[attribute] = setTimeout(function()
													{
														self.getNextStep(attribute, next_value, final_value, step, func, fifo, loop_value);
													}, this.SCALE_TIME);
			}			
		}
		else
		{
			if(loop_value != -1)
			{
				self = this;

				if(this.timeouts[attribute])
				{
					this.timeouts[attribute] = setTimeout(function()
														{
															self.getNextStep(attribute, value, loop_value, -step, func, fifo, value);
														}, this.SCALE_TIME);
				}
			}
			else
			{
				this.timeouts[attribute] = false;
				if(fifo)
					fifo.next();
			}
		}
	},	


	/* 	Function: startAnimationV

		Se trata de una función de *BAJO NIVEL* que empieza una animación.
		Es de bajo nivel por que el usuario *NUNCA LLAMARÁ A ESTA FUNCIÓN*, ya que hay creadas otras funciones más sencillas de usar que llaman a ésta.

		Se trata de una adaptación de la función <startAnimation> para animaciones en las que los valores implicados son *vectores*.
		Por ejemplo, para cambiar un color que es un conjunto de 3 colores *rgb(R,G,B)*.
		La función llama a otra función (<getNextStepV>) una serie de veces.

		Parámetros:
		attribute - Atributo que queremos que inicie la animación.
		final_values - Valor con el que ha de acabar la animación *(sin unidades)*.
		steps - Incremento, cuánto va a ir incrementando el atributo en cada paso.
		func - Función que se llamará en cada paso (que deberá representar la animación).
		fifo - Cola de funciones (ver <xFuncFIFO>).
		loop - *booleano*. Indica si queremos que la animación se meta en un bucle infinito.

		Pasos:
		- Si había otra animación para el mismo atributo, la para.
		- Adquiere el valor actual de nuestro div para dicho atributo.
		- Si se va a realizar algún paso, llama al primer paso, quién llamará al segundo y así sucesivamente.
		- Si no se va a realizar ningún paso, llama a la siguiente función de la Cola <xFuncFIFO>
		

		Devuelve:
		Nada.

		Ejemplos:
		(start code)
		fifo = new xFuncFIFO;
		func = function(r, g, b) { return "rgb(" + r + ", " + g + ", " + b + ")"; };
		xobj.startAnimation("color", [20, 50, 20], 2, functionSe trata de una adaptación de la función <startAnimation> para animaciones en las que los valores implicados son *vectores*.
		Por ejemplo, para cambiar un color que es un conjunto de 3 colores *rgb(R,G,B)*.(xobj, v) { xobj.setStyle("color", func(v[0], v[1], v[2])); }, fifo, false);
		// Creará una animación en la que la anchura del div irá creciendo de 2 en 2.
		// Además al final llamará a la siguiente función de la cola fifo.
		(end)
	*/
	startAnimationV: function(attribute, final_values, steps, func, fifo, loop)
	{
		if(this.timeouts[attribute])
			window.clearTimeout(this.timeouts[attribute]);

		var values = new Array();
		values = this.getNumeric(this.style[attribute]);

		if(loop == true)
		{
			loop = new Array();
			for(var l = 0; l < values.length; l++)
				loop[l] = values[l];
		}
		else
			loop = -1;

		if(final_values && typeof(final_values) == "object" && steps && typeof(steps) == "object" && final_values.length == steps.length && steps.length == values.length)
		{
			var res = true;

			for(var s in steps)
			{
				if(steps[s] == 0)	
					res = false;
				else if(final_values[s] - values[s] < 0)
					steps[s] = Number(-steps[s]);
			}

			if(res)
			{	
				var self = this;
				this.timeouts[attribute] = setTimeout(function()
													{
														self.getNextStepV(attribute, values, final_values, steps, func, fifo, loop);
													}, this.SCALE_TIME);
			}
			else
			{
				if(fifo)
					fifo.next();
			}
		}
		else
		{
			if(fifo)
				fifo.next();
		}

	},

	/* 	Function: getNextStepV
		
		Se trata de una función de *BAJO NIVEL*, a la cuál llamará la función <startAnimation>.
		Es de bajo nivel por que el usuario *NUNCA LLAMARÁ A ESTA FUNCIÓN*.

		Se trata de una adaptación de la función <getNextStep> para animaciones en las que los valores implicados son *vectores*.
		Por ejemplo, para cambiar un color que es un conjunto de 3 colores *rgb(R,G,B)*.
		Esta función es llamada cada 'paso' de la animación. Es decir, si la animación tiene 20 pasos, esta función será llamada 20 veces.
		Es la encargada de cambiar los atributos del div, por medio de una funcion; y de llamarse recursivamente en caso de haber más pasos.		

		Parámetros:
		attribute - Atributo que queremos que inicie la animación.
		value - Valor del atributo en el momento actual *(sin unidades)*
		final_value - Valor con el que ha de acabar la animación *(sin unidades)*.
		step - Incremento, cuánto va a ir incrementando el atributo en cada paso.
		func - Función que se llamará en cada paso (que deberá representar la animación).
		fifo - Cola de funciones (ver <xFuncFIFO>).
		loop - *int* Es el valor Inicial de la animación (esto se evalua automáticamente en <startAnimation>.

		Pasos:
		- Evaluar si hay que continuar.

		_Sí_ : 
		- Calcular próximo valor de la animación.
		- Llamar a la función "func" con dicho valor.
		- Llamar recursivamente a <getNextStep> con los nuevos datos.

		_No_ :
		- Si 'loop' está activado, volver a empezar la animacíon desde el final_value hasta el value inicial de <startAnimation>
		- Si no hay loop llamar a la siguiente función de la fifo (<xFuncFIFO>).

		Devuelve:
		Nada.

		Ejemplos:
		No tiene sentido poner ejemplos, ya que el usuario nunca tendrá que utilizarla.
	*/
	getNextStepV: function(attribute, values, final_values, steps, func, fifo, loop_values)
	{
		var end = 0;
		var next_values = new Array(values.length);
		var self = this;

		for(var i = 0; i < values.length; i++)
		{
			var cont = ((final_values[i] - values[i]) / steps[i]);

			if(cont > 0)
			{
				if(cont >= 1)
					next_values[i] = Number(Number(values[i]) + Number(steps[i]));
				else
					next_values[i] = Number(final_values[i]);
			}
			else
			{
				next_values[i] = values[i];
				end ++;
			}
		}

		if(end != 3)
		{
			func.call(this, next_values);

			self = this;
			if(this.timeouts[attribute])
			{
				this.timeouts[attribute] = setTimeout(function()
														{
															self.getNextStepV(attribute, next_values, final_values, steps, func, fifo, loop_values);
														}, this.SCALE_TIME);
			}
		}
		else
		{
			if(loop_values != -1)
			{
				if(this.timeouts[attribute])
				{
					for(var s = 0; s < steps.length; s++)
						steps[s] = -steps[s];

					this.timeouts[attribute] = setTimeout(function()
															{
																self.getNextStepV(attribute, values, loop_values, steps, func, fifo, values);
															}, this.SCALE_TIME);
				}
			}
			else
			{
				this.timeouts[attribute] = false;
				if(fifo)
					fifo.next();
			}
		}
	},		

	/* 	Function: startPercEffect

		Se trata de una función de *BAJO NIVEL*, a la cuál llamará la función <start>.
		Es de bajo nivel por que el usuario *NUNCA LLAMARÁ A ESTA FUNCIÓN*.

		Esta función aplica un efecto de 'rebote' (con un cierto porcentaje) a una animación dada.
		Es decir, para una animación en la que el div tiene que ir de *A* a *B*, dado un %, hará lo siguiente

		> A·        B
		> A ·       B
		> A  ·      B
		> A   ·     B
		> A    ·    B
		> A     ·   B
		> A      ·  B
		> A       · B
		> A        ·B
		> A·      · B
		> A·     ·  B
		> A·      · B
		> A·       ·B

		Los parámetros son los mismos que <startAnimation>, ya que hará 3 llamadas a dicha función con distintos valores de final_value.

		Parámetros:
		attribute - Atributo que queremos que inicie la animación.
		final_value - Valor con el que ha de acabar la animación *(sin unidades)*.
		step - Incremento, cuánto va a ir incrementando el atributo en cada paso.
		perc- Porcentaje de rebote.
		func - Función que se llamará en cada paso (que deberá representar la animación).
		fifo - Cola de funciones (ver <xFuncFIFO>).
		loop - *booleano*. Indica si queremos que la animación se meta en un bucle infinito.

		Pasos:
		El algoritmo es sencillo, llama 3 veces a la función <startAnimation>

		1ª - ...Llega hasta el final.
		2ª - .Llega hasta el final - el porcentaje de rebote.
		3ª - ...Llega hasta el final.

		Devuelve: 
		Nada.

		Ejemplo:
		(start code)
		xobj.startPercEffect("opacity", 50, 2, 2, function(xobj, v) { xobj.setStyle("opacity", v); }, false, false);
		(end)
	*/
	startPercEffect: function(attribute, final_value, step, perc, func, fifo, loop)
	{
		if(perc > 0)
		{
			var perc_fifo = new xFuncFIFO;
			var self = this;
			
			var new_final_value = (final_value - (step * perc));

			perc_fifo.add(function() { self.startAnimation(attribute, final_value, step, func, perc_fifo) }, self);
			perc_fifo.add(function() { self.startAnimation(attribute, new_final_value, step, func, perc_fifo); }, self);
			perc_fifo.add(function() { self.startAnimation(attribute, final_value, step, func, fifo, loop) }, self);
			perc_fifo.next();
		}
		else
			this.startAnimation(attribute, final_value, step, func, fifo, loop);
	},

	/* 	Function: start
		
		Se trata de una función de *BAJO NIVEL*, a la cuál llamará cualquiera las funciones
		Es de bajo nivel por que el usuario *NUNCA LLAMARÁ A ESTA FUNCIÓN*.

		Esta función analiza los parámetros y decide si llamar a <startAnimation> o a <startAnimationV>, según final_value sea un vector o no.
		Además, para cada tipo de animación crea una función _func_ que será la encargada de actualizar el estilo del div en cada paso.

		Parámetros:
		attribute - Atributo que queremos que inicie la animación.
		final_value - Valor con el que ha de acabar la animación *(CON unidades)*.
		scale - *Número* de pasos
		perc- Porcentaje de rebote.
		fifo - Cola de funciones (ver <xFuncFIFO>).
		loop - *booleano*. Indica si queremos que la animación se meta en un bucle infinito.

		Devuelve:
		Nada.
	*/
	start: function(attribute, final_value, scale, perc, fifo, loop)
	{
		var value_n = this.getNumeric(this.style[attribute]);
		var self = this;

		if(navigator.userAgent.toLowerCase().indexOf('msie') != -1)
			scale = parseInt(scale / 2);

		if(value_n && value_n.length && value_n.length > 1)
		{
			var steps = new Array()
			
			for(var i = 0; i < value_n.length; i++)
			{
				steps[i] = parseInt((this.getNumeric(final_value[i]) - value_n[i]) / scale);
				if(steps[i] < 0)
					steps[i] = -steps[i];

				if(steps[i] < 1)
					steps[i] = 1;
			}

			this.startAnimationV(attribute, final_value, steps,
				function(value) 
				{ 
					// RGB
					self.setStyle(attribute, "rgb(" + value[0] + ", " + value[1] + ", " + value[2] + ")");
				}, fifo, loop);
		}
		else
		{
			var final_value_n = this.getNumeric(final_value);

			var step = parseInt((final_value_n - value_n) / scale);
			if(step < 0)
				step = -step;
			
			if(step < 1)
				step = 1;

			this.startPercEffect(attribute, final_value_n, step, perc,
					function(value)
					{
						value = value >= 0 ? value : 0;
						self.setStyle(attribute, value + self.getUnit(final_value));
					}, fifo, loop);

		}
	},

	/* 	Function: moveRelX
		Animación para el movimiento relativo (con *position: relative*) en el eje X.

		Esta animación se basa en cambiar el márgen izquierdo del div.

		Parámetros:
		final_x - Valor con el que ha de acabar la animación *(CON unidades)*.
		scale - *Número* de pasos.
		perc- Porcentaje de rebote.
		fifo - Cola de funciones (ver <xFuncFIFO>).
		loop - *booleano*. Indica si queremos que la animación se meta en un bucle infinito.

		Devuelve:
		Nada.
	*/
	moveRelX: function(final_x, scale, perc, fifo, loop)
	{
		this.start("rel_pos_x", final_x, scale, perc, fifo, loop);
	},


	/* 	Function: moveRelY
		Animación para el movimiento relativo (con *position: relative*) en el eje Y.

		Esta animación se basa en cambiar el márgen superior.

		Parámetros:
		final_y - Valor con el que ha de acabar la animación *(CON unidades)*.
		scale - *Número* de pasos.
		perc- Porcentaje de rebote.
		fifo - Cola de funciones (ver <xFuncFIFO>).
		loop - *booleano*. Indica si queremos que la animación se meta en un bucle infinito.

		Devuelve:
		Nada.
	*/
	moveRelY: function(final_y, scale, perc, fifo, loop)
	{
		this.start("rel_pos_y", final_y, scale, perc, fifo, loop);
	},

	/* 	Function: moveRel
		Animación para el movimiento relativo (con *position: relative*) en ambos ejes.

		Esta animación se basa en cambiar los márgenes del div.

		Parámetros:
		final_x - Valor con el que ha de acabar la animación *(CON unidades)* (X).
		final_y - Valor con el que ha de acabar la animación *(CON unidades)* (Y).
		scale - *Número* de pasos.
		perc- Porcentaje de rebote.
		fifo - Cola de funciones (ver <xFuncFIFO>).
		loop - *booleano*. Indica si queremos que la animación se meta en un bucle infinito.

		Devuelve:
		Nada.
	*/
	moveRel: function(final_x, final_y, scale, perc, fifo, loop)
	{
		this.start("rel_pos_x", final_x, scale, perc, fifo, loop);
		this.start("rel_pos_y", final_y, scale, perc, false, loop);
	},

	/* 	Function: moveAbsX
		Animación para el movimiento absoluto (con *position: absolute*) en el eje X.

		Esta animación se basa en cambiar el atributo _left_ del div.

		Parámetros:
		final_x - Valor con el que ha de acabar la animación *(CON unidades)* (X).
		scale - *Número* de pasos.
		perc- Porcentaje de rebote.
		fifo - Cola de funciones (ver <xFuncFIFO>).
		loop - *booleano*. Indica si queremos que la animación se meta en un bucle infinito.

		Devuelve:
		Nada.
	*/
	moveAbsX: function(final_x, scale, perc, fifo, loop)
	{
		this.start("abs_pos_x", final_x, scale, perc, fifo, loop);
	},

	/* 	Function: moveAbsY
		Animación para el movimiento absoluto (con *position: absolute*) en el eje Y.

		Esta animación se basa en cambiar el atributo _top_ del div.

		Parámetros:
		final_y - Valor con el que ha de acabar la animación *(CON unidades)* (Y).
		scale - *Número* de pasos.
		perc- Porcentaje de rebote.
		fifo - Cola de funciones (ver <xFuncFIFO>).
		loop - *booleano*. Indica si queremos que la animación se meta en un bucle infinito.

		Devuelve:
		Nada.
	*/
	moveAbsY: function(final_y, scale, perc, fifo, loop)
	{
		this.start("abs_pos_y", final_y, scale, perc, fifo, loop);
	},

	/* 	Function: moveAbs
		Animación para el movimiento absoluto (con *position: absolute*).

		Esta animación se basa en cambiar el atributo _top_ y _left_ del div.

		Parámetros:
		final_x - Valor con el que ha de acabar la animación *(CON unidades)* (X).
		final_y - Valor con el que ha de acabar la animación *(CON unidades)* (Y).
		scale - *Número* de pasos.
		perc- Porcentaje de rebote.
		fifo - Cola de funciones (ver <xFuncFIFO>).
		loop - *booleano*. Indica si queremos que la animación se meta en un bucle infinito.

		Devuelve:
		Nada.
	*/
	moveAbs: function(final_x, final_y, scale, perc, fifo, loop)
	{
		this.start("abs_pos_x", final_x, scale, perc, fifo, loop);
		this.start("abs_pos_y", final_y, scale, perc, false, loop);
	},

	/* 	Function: align
		Animación para alinear un div movimientos _absolutos_.

		Parámetros:
		position - Posicion en la que queremos situar el div.
		scale - *Número* de pasos.
		perc- Porcentaje de rebote.
		fifo - Cola de funciones (ver <xFuncFIFO>).

		Posiciones:
		top - Arriba.
		bottom - Abajo.
		left - A la izquierda.
		center - Centrado (X e Y).
		center_x - Centrado (X).
		center_y - Centrado (Y).
		right - A la derecha.

		Devuelve:
		Nada.
	*/
	align: function(position, scale, perc, fifo)
	{
        var obj_perc_x = 0;
        var obj_perc_y = 0;
        var center_pos_x = 0;
        var center_pos_y = 0;
		
		var height = this.getNumeric(this.style.height);
		var width = this.getNumeric(this.style.width);

		switch(position)
		{
			case "top":
				this.moveAbsY("0%", scale, perc, fifo);
				break;

			case "bottom":
				obj_perc_y = parseInt((height / screen.availHeight) * 100);
				pos_y = parseInt(100 - obj_perc_y) + "%";
				this.moveAbsY(pos_y, scale, perc, fifo);
				break;

			case "left":
				this.moveAbsX("0%", scale, perc, fifo);
				break;

			case "center":
				obj_perc_x = parseInt((width / screen.availWidth) * 100);
				center_pos_x = parseInt(50 - parseInt(obj_perc_x / 2)) + "%";

				obj_perc_y = parseInt((height / screen.availHeight) * 100);
				center_pos_y = parseInt(50 - parseInt(obj_perc_y / 2)) + "%";

				if(scale != 1)
					this.moveAbs(center_pos_x, center_pos_y, scale, perc, fifo);
				else
				{
					this.setStyle("abs_pos_x", center_pos_x);
					this.setStyle("abs_pos_y", center_pos_y);
				}
				
				break;

			case "center_x":
				obj_perc_x = parseInt((width / screen.availWidth) * 100);
				center_pos_x = parseInt(50 - parseInt(obj_perc_x / 2)) + "%";

				this.moveAbsX(center_pos_x, scale, perc, fifo);
				break;

			case "center_y":
				obj_perc_y = parseInt((height / screen.availHeight) * 100);
				center_pos_y = parseInt(50 - parseInt(obj_perc_y / 2)) + "%";

				this.moveAbsY(center_pos_y, scale, perc, fifo);
				break;

			case "right":
				obj_perc_x = parseInt((height / screen.availWidth) * 100);
				pos_x = parseInt(100 - obj_perc_x) + "%";

				this.moveAbsX(pos_x, scale, perc, fifo);
				break;
		}

	},

	/* 	Function: alignRel
		Animación para alinear un div movimientos _relativos_.

		Parámetros:
		position - Posicion en la que queremos situar el div.
		scale - *Número* de pasos.
		perc- Porcentaje de rebote.
		fifo - Cola de funciones (ver <xFuncFIFO>).

		Posiciones:
		top - Arriba.
		bottom - Abajo.
		left - A la izquierda.
		center - Centrado (X e Y).
		center_x - Centrado (X).
		center_y - Centrado (Y).
		right - A la derecha.

		Devuelve:
		Nada.
	*/
	alignRel: function(position, scale, perc, fifo)
	{
        var obj_perc_x = 0;
        var obj_perc_y = 0;
        var center_pos_x = 0;
        var center_pos_y = 0;
		
		switch(position)
		{
			case "top":
				this.moveAbsY("0%", scale, perc, fifo);
				break;

			case "bottom":
				obj_perc_y = parseInt((this.div_cont.offsetHeight / screen.availHeight) * 100);
				pos_y = parseInt(100 - obj_perc_y) + "%";
				this.moveRelY(pos_y, scale, perc, fifo);
				break;

			case "left":
				this.moveRelX("0%", scale, perc, fifo);
				break;

			case "center":
				obj_perc_x = parseInt((this.div_cont.offsetWidth / screen.availWidth) * 100);
				center_pos_x = parseInt(50 - parseInt(obj_perc_x / 2)) + "%";

				obj_perc_y = parseInt((this.div_cont.offsetHeight / screen.availHeight) * 100);
				center_pos_y = parseInt(50 - parseInt(obj_perc_y / 2)) + "%";

				if(scale != 1)
					this.moveRel(center_pos_x, center_pos_y, scale, perc, fifo);
				else
				{
					this.setStyle("abs_rel_x", center_pos_x);
					this.setStyle("abs_rel_y", center_pos_y);
				}
				
				break;

			case "center_x":
				obj_perc_x = parseInt((this.div_cont.offsetWidth / screen.availWidth) * 100);
				center_pos_x = parseInt(50 - parseInt(obj_perc_x / 2)) + "%";

				this.moveRelX(center_pos_x, scale, perc, fifo);
				break;

			case "center_y":
				obj_perc_y = parseInt((this.div_cont.offsetHeight / screen.availHeight) * 100);
				center_pos_y = parseInt(50 - parseInt(obj_perc_y / 2)) + "%";

				this.moveRelY(center_pos_y, scale, perc, fifo);
				break;

			case "right":
				obj_perc_x = parseInt((this.div_cont.offsetWidth / screen.availWidth) * 100);
				pos_x = parseInt(100 - obj_perc_x) + "%";

				this.moveRelX(pos_x, scale, perc, fifo);
				break;
		}
	},


	/* 	Function: setWidth
		Animación para cambiar la anchura de un div.

		Esta animación se basa en cambiar el atributo _width_ del div.

		Parámetros:
		final_w - Valor con el que ha de acabar la animación *(CON unidades)* (W).
		scale - *Número* de pasos.
		perc- Porcentaje de rebote.
		fifo - Cola de funciones (ver <xFuncFIFO>).
		loop - *booleano*. Indica si queremos que la animación se meta en un bucle infinito.

		Devuelve:
		Nada.
	*/	
	setWidth: function(final_w, scale, perc, fifo, loop)
	{
		this.start("width", final_w, scale, perc, fifo, loop);
	},

	/* 	Function: setHeight
		Animación para cambiar la altura de un div.

		Esta animación se basa en cambiar el atributo _height_ del div.

		Parámetros:
		final_h - Valor con el que ha de acabar la animación *(CON unidades)* (H).
		scale - *Número* de pasos.
		perc- Porcentaje de rebote.
		fifo - Cola de funciones (ver <xFuncFIFO>).
		loop - *booleano*. Indica si queremos que la animación se meta en un bucle infinito.

		Devuelve:
		Nada.
	*/	
	setHeight: function(final_w, scale, perc, fifo, loop)
	{
		this.start("height", final_w, scale, perc, fifo, loop);
	},
		
	/* 	Function: resize
		Animación para cambiar la anchura y la altura de un div.

		Esta animación se basa en cambiar el atributo _width_ y _height_ del div.

		Parámetros:
		final_w - Valor con el que ha de acabar la animación *(CON unidades)* (W).
		final_h - Valor con el que ha de acabar la animación *(CON unidades)* (H).
		scale - *Número* de pasos.
		perc- Porcentaje de rebote.
		fifo - Cola de funciones (ver <xFuncFIFO>).
		loop - *booleano*. Indica si queremos que la animación se meta en un bucle infinito.

		Devuelve:
		Nada.
	*/	
	resize: function(final_w, final_h, scale, perc, fifo, loop)
	{
		this.start("width", final_w, scale, perc, fifo, loop);
		this.start("height", final_h, scale, perc, false, loop);
	},


	/* 	Function: setBorderWidth
		Animación para cambiar la anchura del borde de un div.

		Esta animación se basa en cambiar el atributo _borderWidth_ del div.

		Parámetros:
		final_w - Valor con el que ha de acabar la animación *(CON unidades)* (W).
		scale - *Número* de pasos.
		fifo - Cola de funciones (ver <xFuncFIFO>).
		loop - *booleano*. Indica si queremos que la animación se meta en un bucle infinito.

		Devuelve:
		Nada.
	*/	
	setBorderWidth:	function(final_w, scale, fifo, loop)
	{
		this.start("borderWidth", final_w, scale, 0, fifo, loop);
	},

	/* 	Function: setOpacity
		Animación para cambiar la opacidad de un div

		Parámetros:
		final_o - Valor con el que ha de acabar la animación *(CON unidades)* (O).
		scale - *Número* de pasos.
		fifo - Cola de funciones (ver <xFuncFIFO>).
		loop - *booleano*. Indica si queremos que la animación se meta en un bucle infinito.

		Devuelve:
		Nada.
	*/	
	setOpacity:	function(final_o, scale, fifo, loop)
	{
		this.start("opacity", final_o, scale, 0, fifo, loop);
	},


	/* 	Function: setBorderColor
		Animación para cambiar el color de borde de un div.

		Esta animación se basa en cambiar _borderColor_.

		Parámetros:
		final_colors - Valor con el que ha de acabar la animación *(CON unidades)*. Hay varias formas de definir un color.
		scale - *Número* de pasos.
		fifo - Cola de funciones (ver <xFuncFIFO>).
		loop - *booleano*. Indica si queremos que la animación se meta en un bucle infinito.

		final_colors:
		Hexadecimal - "#123BDA".
		RGB - "rgb(255, 255, 0)".
		Vector - [255, 255, 0].

		Devuelve:
		Nada.
	*/	
	setBorderColor: function(final_colors, scale, fifo, loop)
	{
		if(typeof(final_colors) == "string")
		{
			var rgb = this.getRGB(final_colors);
			final_colors = this.getNumeric(rgb);
		}
		this.start("borderColor", final_colors, scale, 0, fifo, loop);
	},

	/* 	Function: setBackgroundColor
		Animación para cambiar el color de fondo de un div.

		Esta animación se basa en cambiar _backgroundColor_.

		Parámetros:
		final_colors - Valor con el que ha de acabar la animación *(CON unidades)*. Hay varias formas de definir un color.
		scale - *Número* de pasos.
		fifo - Cola de funciones (ver <xFuncFIFO>).
		loop - *booleano*. Indica si queremos que la animación se meta en un bucle infinito.

		final_colors:
		Hexadecimal - "#123BDA".
		RGB - "rgb(255, 255, 0)".
		Vector - [255, 255, 0].

		Devuelve:
		Nada.
	*/	
	setBackgroundColor: function(final_colors, scale, fifo, loop)
	{
		if(typeof(final_colors) == "string")
		{
			var rgb = this.getRGB(final_colors);
			final_colors = this.getNumeric(rgb);
		}

		this.start("backgroundColor", final_colors, scale, 0, fifo, loop);
	},

	/* 	Function: setColor
		Animación para cambiar el color de letra de un div.

		Esta animación se basa en cambiar _color_.

		Parámetros:
		final_c - Valor con el que ha de acabar la animación *(CON unidades)*. Hay varias formas de definir un color.
		scale - *Número* de pasos.
		fifo - Cola de funciones (ver <xFuncFIFO>).
		loop - *booleano*. Indica si queremos que la animación se meta en un bucle infinito.

		final_c:
		Hexadecimal - "#123BDA".
		RGB - "rgb(255, 255, 0)".
		Vector - [255, 255, 0].

		Devuelve:
		Nada.
	*/	
	setColor:	function(final_c, scale, fifo, loop)
	{
		if(typeof(final_c) == "string")
		{
			var rgb = this.getRGB(final_c);
			final_c = this.getNumeric(rgb);
		}

		this.start("color", final_c, scale, 0, fifo, loop);
	},


	/* 	Function: stop
		Este método para una animación

		Parámetros:
		attribute - Animación a parar.

		Devuelve:
		Nada.
	*/	
	stop: function(attribute)
	{
		if(this.timeouts[attribute])
		{
			window.clearTimeout(this.timeouts[attribute]);
			this.timeouts[attribute] = false;
		}
	},

	/* 	Function: stopAll
		Este método para todas las animaciones

		Devuelve:
		Nada.
	*/	
	stopAll: function()
	{
		for(var t in this.timeouts)
		{
			window.clearTimeout(this.timeouts[t]);
			this.timeouts[t] = false;
		}
	},

	/* 	Function: destroy
		Este método destruye el div, haciéndolo transparente y borrándolo.

		Parámetros:
		scale - Pasos para hacer el div transparente.

		Devuelve:
		Nada.
	*/	
	destroy: function(scale)
	{
		if(!scale)
			scale = 20;

		var fifo = new xFuncFIFO;
		var self = this;

		fifo.add(function() { self.setOpacity("0", scale, fifo, false) }, this);
		fifo.add(function() 
		{ 
			this.div_cont.parentNode.removeChild(this.div_cont);
		}, this);

		fifo.next();
	},

	/* 	Function: getCSS
		Este método consigue el CSS asociado al Div.

		Devuelve:
		Nada.
	*/	
	getCSS: function()
	{
		this.css = false;
	
		if(this.div_cont && this.div_cont.currentStyle)
		{
			this.css = this.div_cont.currentStyle;
		}
		else if(window.getComputedStyle)
		{
			this.css = window.getComputedStyle(this.div_cont, null);
		}
	},

	/* 	Function: getCSS
		Este método convierte un color en cualquiera de los formatos aceptados en un Vector de colores.

		Parámetros:
		color - color en cualquiera de los formatos aceptados (los usados en <setBorderColor>, <setBackgroundColor> y <setColor>).
		
		Formatos:
		Hexadecimal - "#123BDA".
		RGB - "rgb(255, 255, 0)".
		Vector - [255, 255, 0].

		Devuelve:
		Color en formato RGB.
	*/	
	getRGB: function(color)
	{
		var res = false;

		if(color)
		{				
			if(color.match("rgb"))
			{
				res = color;
			}
			else if(color.match("#"))
			{
				var rgb = new Array(3);

				var hex_str = new Array();
				hex_str = color.match(/[^#]/g);

				var i, j;
				for(i = 0, j = 0; i < 3; i ++, j += 2)
				{
					rgb[i] = (this.getHEX2RGB(hex_str[j]) * 16 + this.getHEX2RGB(hex_str[j + 1]) );

				}

				res = "rgb(" + rgb[0] + ", " + rgb[1] + ", " + rgb[2] + ")";
			}
			else
				res = "rgb(0, 0, 0)";
	
		}

		return res;
	},

	/* 	Function: getHEX2RGB
		Este método convierte un número Hexadecimal en RGB.

		Parámetros:	
		hex - Número en hexadecimal.

		Devuelve:
		Número RGB.
	*/
	getHEX2RGB: function(hex)
	{
		res = hex ? Number(HEX_TABLE[hex.toUpperCase()]) : 0;
		
		return res;
	},


	/* 	Function: getNumeric
		Este método extrae la parte numérica.

		Parámetros:	
	 	string - valor

		Devuelve:
		Parte numérica.

		Ejemplo:
		*100* px. rgb(*255*, *255*, *5*).
		
	*/
	getNumeric: function(string)
	{
		return String(string).match(/[0-9]+/g);
	},

	/* 	Function: getUnit
		Este método extrae la unidad

		Parámetros:	
	 	string - valor

		Devuelve:
		Unidad.

		Ejemplo:
		100 *px*.
	*/
	getUnit: function(string)
	{
		return String(string).match(/[^,\.0-9]+/g);
	}
}

/* 	Class: XLayer
	Se trata de una capa que oscurece la pantalla y no permite acceder a los elementos por debajo de ella.
*/

/* Constructor: XLayer
	
	Parámetros:
	div_cont - Div que contendrá la XLayer (normalmente el body).
	class_name - Clase de CSS asociada al XLayer.
	opacity - Opacidad de la capa.
	fifo - Cola de funciones (ver <xFuncFIFO>).
*/
function XLayer(div_cont, class_name, opacity, fifo)
{
	var div = document.createElement("div");
	div.className = class_name;

	div.style.position = "absolute";
	document.body.style.overflow = "hidden";

	this.xdiv = new XObject(div);
	
	this.xdiv.setStyle("width", "100%");
	this.xdiv.setStyle("height", "100%");

	this.xdiv.setStyle("opacity", opacity);
	//this.xdiv.setOpacity(opacity, 1, fifo);

	div_cont.appendChild(div);
}

XLayer.prototype =
	{
		/* 	Function: stop
			Borra la capa.

			Parámetros:
			scale - Pasos para que desaparezca

			Devuelve: 
			Nada.
		*/
		stop:	function(scale)
		{
			document.body.style.overflowY = "auto";

			this.xdiv.destroy(scale);
		}
	};

